/*
 * Example_05.cpp
 *
 *  Created on: Mar 14, 2009
 *      Author: Lev
 */

#include <iostream>
#include <exception>
#include <string>
#include <sstream>
#include <pthread.h>
using namespace std;

struct mutex_error : public exception {
	string _message;
	mutex_error( const char *message ) {
		_message = "mutex error: ";
		_message += message;
	}
	const char* what() const throw() {
		return _message.c_str();
	}
	~mutex_error() throw() {}
};

template<typename T>
class ProtectedData {
private:
	pthread_mutex_t _mutex;
	int _status;
	T _data;
public:
	ProtectedData( const T &data ) throw(mutex_error) : _data(data) {
		_status = pthread_mutex_init( &_mutex, NULL );
		if( _status != 0 )
			throw mutex_error( "pthread_mutex_init" );
	}
	~ProtectedData() throw() {
		if( _status == 0 ) {
			_status = pthread_mutex_destroy( &_mutex );
		}
	}
	bool readData( T &buffer ) throw(mutex_error) {
		bool done = false;
		int status = pthread_mutex_lock( &_mutex );
		if( status != 0 )
			throw mutex_error( "pthread_mutex_lock" );
		buffer = _data;
		done = true;
		status = pthread_mutex_unlock( &_mutex );
		if( status != 0 )
			throw mutex_error( "pthread_mutex_unlock" );
		return done;
	}
	bool writeData( const T &data ) throw(mutex_error) {
		bool done = false;
		int status = pthread_mutex_lock( &_mutex );
		if( status != 0 )
			throw mutex_error( "pthread_mutex_lock" );
		_data = data;
		done = true;
		status = pthread_mutex_unlock( &_mutex );
		if( status != 0 )
			throw mutex_error( "pthread_mutex_unlock" );
		return done;
	}
};

void *thread_routine( void *arg ) {
	int id = *(int *)arg;
	ostringstream oss( ostringstream::out );
	oss << "thread_routine " << id << ": started" << endl;
	cout << oss.str();
	ProtectedData<double> proData( 123.0 );
	for( int i = 0; i < 5; i++ ) {
		ostringstream oss1( ostringstream::out );
		if( i % 2 == 0 ) {
			oss1 << "thread_routine " << id << ": writing data ... ";
			if( proData.writeData( (double)i ) )
				oss1 << (double)i << endl;
			else
				oss1 << "failed" << endl;
		} else {
			oss1 << "thread_routine " << id << ": reading data ... ";
			double data;
			if( proData.readData( data ) )
				oss1 << data << endl;
			else
				oss1 << "failed" << endl;
		}
		cout << oss1.str();
	}
	oss.seekp( ios::beg );
	oss << "thread_routine " << id << ": completed" << endl;
	cout << oss.str();
	return arg;
}

//int main() {
//	const int N = 3;
//	pthread_t threadId[N];
//	void *threadResult[N];
//	int status, id[N];
//
//	for( int i = 0; i < N; i++ ) {
//		ostringstream oss( ostringstream::out );
//		id[i] = i + 1;
//		oss << "pthread_create " << id[i] << " ... " << endl;
//		cout << oss.str();
//		status = pthread_create( &threadId[i], NULL, thread_routine, &id[i] );
//		if( status != 0 ) {
//			oss.seekp( ios::beg );
//			oss << "pthread_create " << id[i] << ": failed: " << status << endl;
//			cout << oss.str();
//			return status;
//		}
//	}
//	for( int i = 0; i < N; i++ ) {
//		status = pthread_join( threadId[i], &threadResult[i] );
//		if( status != 0 ) {
//			ostringstream oss( ostringstream::out );
//			oss << "pthread_join " << i + 1 << ": failed: " << status << endl;
//			cout << oss.str();
//			return status;
//		}
//	}
//	return 0;
//}
